﻿using System;
using System.Linq;
using System.Xml;
using DataScripter.Database;
using DataScripter.Scripting;
using DataScripter.Scripting.Fluent;
using DataScripter.Scripting.InputSources;
using DataScripter.Scripting.Output;
using DataScripter.Scripting.Output.Sql.SqlServer;
using DataScripter.Scripting.OutputDestinations;
using DataScripter.Scripting.ScriptDefinitions;
using DataScripter.XmlInterpreter;

namespace DataScripter.UI.Old
{
    // ReSharper disable UnusedMember.Global
    class XmlEngineConfiguratorv1 : IXmlEngineConfigurator
    // ReSharper restore UnusedMember.Global
    {
        public IEngine Configure(IEngine engine, XmlDocument document, ScriptOptions scriptOptions)
        {
            XmlElement script = document.DocumentElement;
            if (script == null)
            {
                return engine;
            }

            string connectionString = script.GetAttribute("ConnectionString");
            string ifRecordExistsStrategyAttributeValue = script.GetAttribute("IfRecordExists");
            IfRecordExistsStrategy ifRecordExistsStrategy = Util.Enum.Parse(ifRecordExistsStrategyAttributeValue,
                                                                            IfRecordExistsStrategy.None);
            SqlServerScriptOptions sqlServerScriptOptions = (SqlServerScriptOptions)scriptOptions;
            switch (ifRecordExistsStrategy)
            {
                case IfRecordExistsStrategy.Skip:
                    sqlServerScriptOptions.SqlScriptType = SqlScriptType.SkipIfExistsElseInsert;
                    break;
                case IfRecordExistsStrategy.None:
                    sqlServerScriptOptions.SqlScriptType = SqlScriptType.Insert;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            string outputPath = script.GetAttribute("OutputPath");

            scriptOptions.ScriptOutputType = ScriptOutputType.File;
            scriptOptions.Filename = outputPath;

            SqlServerInputSource inputSource = new SqlServerInputSource("Database", engine);
            engine.RegisterType<IInputSource>("a", inputSource);
            engine.RegisterType<ISqlServerInputSource>("b", inputSource);
            inputSource.ConnectionString = connectionString;

            FileOutputDestination outputDestination = new FileOutputDestination("", engine);
            engine.RegisterType<IOutputDestination>(outputDestination);

            IScriptDefinition scriptDefinition = new ScriptDefinition("", engine);
            engine.RegisterType(scriptDefinition);
            
            foreach (XmlNode node in script.ChildNodes)
            {
                XmlElement element = node as XmlElement;
                if (element == null)
                    continue;

                switch (element.Name)
                {
                    case "DataProducer":
                        string typeDescriptor = element.GetAttribute("Type");
                        string name = element.GetAttribute("Name");
                        if (string.IsNullOrEmpty(name))
                        {
                            engine.RegisterType<IDataProducer>(Type.GetType(typeDescriptor));
                        }
                        else
                        {
                            engine.RegisterType<IDataProducer>(name, Type.GetType(typeDescriptor));
                        }
                        break;
                    case "OutputProducer":
                        typeDescriptor = element.GetAttribute("Type");
                        name = element.GetAttribute("Name");
                        if (string.IsNullOrEmpty(name))
                        {
                            engine.RegisterType<IOutputProducer>(Type.GetType(typeDescriptor));
                        }
                        else
                        {
                            engine.RegisterType<IOutputProducer>(name, Type.GetType(typeDescriptor));
                        }
                        break;
                    case "Db":
                        typeDescriptor = element.GetAttribute("Type");
                        name = element.GetAttribute("Name");
                        if (string.IsNullOrEmpty(name))
                        {
                            engine.RegisterType<Db>(Type.GetType(typeDescriptor));
                        }
                        else
                        {
                            engine.RegisterType<Db>(name, Type.GetType(typeDescriptor));
                        }
                        break;
                    case "DatabaseStructure":
                        typeDescriptor = element.GetAttribute("Type");
                        name = element.GetAttribute("Name");
                        if (string.IsNullOrEmpty(name))
                        {
                            engine.RegisterType<IDatabaseStructure>(Type.GetType(typeDescriptor));
                        }
                        else
                        {
                            engine.RegisterType<IDatabaseStructure>(name, Type.GetType(typeDescriptor));
                        }
                        break;
                    case "ScriptRequest":
                        {
                            string tableName = element.GetAttribute("Name");
                            string whereClause = element.GetAttribute("Where");
                            IFluentTableScriptRequest scriptRequest = scriptDefinition.RequestScript(tableName);
                            if (!string.IsNullOrEmpty(whereClause))
                            {
                                scriptRequest.Where(whereClause);
                            }

                            foreach (XmlNode childnode in element.ChildNodes)
                            {
                                XmlElement childElement = childnode as XmlElement;
                                if (childElement == null)
                                    continue;

                                switch (childElement.Name)
                                {
                                    case "Or":
                                        IFluentOrFilterRequest or = scriptRequest.BeginOr();
                                        scriptRequest = InterpretOrElement(childElement, or, inputSource);
                                        break;
                                    case "FilterByForeignKey":
                                        string foreignKeyName = childElement.GetAttribute("Name");
                                        string foreignTable = childElement.GetAttribute("OnTable");
                                        ITableStructure tableStructure = inputSource.Resolve<IDatabaseStructure>().GetTable(string.IsNullOrEmpty(foreignTable) ? tableName : foreignTable);
                                        IForeignKey foreignKey = tableStructure.ForeignKeys.Single(fk => fk.Name.ToUpper() == foreignKeyName.ToUpper());
                                        if (foreignKey == null)
                                        {
                                        }
                                        scriptRequest.FilterByForeignKey(foreignKey);
                                        break;
                                    case "Lookup":
                                        string fieldName = childElement.GetAttribute("Name");
                                        string lookupTable = childElement.GetAttribute("LookupTable");
                                        string lookupField = childElement.GetAttribute("LookupField");
                                        scriptRequest.Lookup(fieldName, lookupTable, lookupField);
                                        break;
                                }
                            }
                        }
                        break;
                }
            }


            return engine;
        }

        private static IFluentTableScriptRequest InterpretOrElement(XmlElement element, IFluentOrFilterRequest or, IInputSource inputSource)
        {
            foreach (XmlNode childnode in element.ChildNodes)
            {
                XmlElement childElement = childnode as XmlElement;
                if (childElement == null)
                    continue;

                switch (childElement.Name)
                {
                    case "FilterByForeignKey":
                        string foreignKeyName = childElement.GetAttribute("Name");
                        string foreignTable = childElement.GetAttribute("OnTable");
                        ITableStructure tableStructure = inputSource.Resolve<IDatabaseStructure>().GetTable(string.IsNullOrEmpty(foreignTable) ? or.TableName : foreignTable);
                        IForeignKey foreignKey = tableStructure.ForeignKeys.Single(fk => fk.Name.ToUpper() == foreignKeyName.ToUpper());
                        if (foreignKey == null)
                        {
                        }
                        or.FilterByForeignKey(foreignKey);
                        break;
                    case "Where":
                        string whereClause = childElement.GetAttribute("Clause");
                        or.Where(whereClause);
                        break;
                }
            }
            return or.EndOr();
        }
    }
}